﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Web;
using System.Web.Mvc;
using Azure.Online.Web.Models.Proxy;
using Azure.Online.Web.Routing;
using Azure.Online.Web.Utils;

namespace Azure.Online.Web.Controllers {
    public class ProxyController : Controller {

        [AcceptVerbs("GET")]
        public ActionResult ExecuteGet() {
            return Process(System.Web.HttpContext.Current);
        }

        [AcceptVerbs("PUT")]
        public ActionResult ExecutePut() {
            return Process(System.Web.HttpContext.Current);
        }

        [AcceptVerbs("POST")]
        public ActionResult ExecutePost() {
            return Process(System.Web.HttpContext.Current);
        }

        [AcceptVerbs("DELETE")]
        public ActionResult ExecuteDelete() {
            return Process(System.Web.HttpContext.Current);
        }

        [AcceptVerbs("GET")]
        public ActionResult Download(string u, string d, string md, string v, string a, string n = null, bool view = false, string mimeType = null) {
            var request = (HttpWebRequest)WebRequest.Create(u);
            if (!string.IsNullOrWhiteSpace(d)) {
                request.Date = DateTime.Parse(d);
            }
            if (!string.IsNullOrWhiteSpace(md)) {
                request.Headers["x-ms-date"] = md;
            }
            request.Headers["x-ms-version"] = v;
            request.Headers["Authorization"] = a;
            return ProcessWebRequest(request, System.Web.HttpContext.Current, response => new ProxyDownloadActionResult(response, !view ? n : null) { ContentType = mimeType });
        }

        private ActionResult Process(HttpContext context) {
            var targetUrl = context.Request.Headers["x-azure-online-url"];
            var targetDate = context.Request.Headers["x-azure-online-date"];
            var downloadName = context.Request.Headers["x-azure-online-download-name"];
            var contentLength = context.Request.Headers["x-azure-online-content-length"];
            var contentType = context.Request.Headers["x-azure-online-content-type"];
            var base64 = context.Request.Headers["x-azure-online-accept-base64"];

            var request = (HttpWebRequest)WebRequest.Create(targetUrl);
            request.Method = context.Request.HttpMethod;

            if (!string.IsNullOrWhiteSpace(targetDate)) {
                request.Date = DateTime.Parse(targetDate);
            }

            if (!string.IsNullOrWhiteSpace(contentType)) {
                request.ContentType = contentType;
            }

            if (!string.IsNullOrWhiteSpace(contentLength)) {
                request.ContentLength = long.Parse(contentLength);
            }

            foreach (string headerName in context.Request.Headers.Keys) {
                switch (headerName) {
                    case "Accept":
                        request.Accept = context.Request.Headers[headerName];
                        break;
                    case "Accept-Charset":
                    case "Authorization":
                    case "DataServiceVersion":
                    case "MaxDataServiceVersion":
                    case "ETag":
                    case "If-Match":
                        request.Headers[headerName] = context.Request.Headers[headerName];
                        break;
                    default:
                        if (headerName.StartsWith("x-ms-")) {
                            request.Headers[headerName] = context.Request.Headers[headerName];
                        }
                        break;
                }
            }
            if (!string.IsNullOrWhiteSpace(downloadName)) {
                return new ContentResult { Content = RegisterDownload(request, downloadName) };
            }
            return ProcessWebRequest(request, context, response => new ProxyActionResult(response) { AcceptBase64 = base64 == "true"});
        }

        private static ActionResult ProcessWebRequest(HttpWebRequest request, HttpContext context, Func<HttpWebResponse, ProxyActionResult> resultBuilder) {
            try {
                if (request.ContentLength >= 0) {
                    var requestStream = request.GetRequestStream();
                    StreamUtils.CopyStream(context.Request.InputStream, requestStream);
                }
                var response = request.GetResponse();
                return resultBuilder((HttpWebResponse)response);
            } catch (SocketException exc) {
                return new HttpNotFoundResult(exc.Message);
            } catch (WebException exc) {
                switch (exc.Status) {
                    case WebExceptionStatus.ProtocolError:
                        return new ProxyActionResult((HttpWebResponse)exc.Response);
                    case WebExceptionStatus.NameResolutionFailure:
                        return new HttpNotFoundResult(exc.Message);
                    default:
                        throw;
                }
            }
        }

        private string RegisterDownload(HttpWebRequest request, string fileName) {
            var id = Guid.NewGuid().ToString();
            HttpContext.Cache[id] = new DownloadRequest { WebRequest = request, FileName = fileName };
            return Url.AzureOnline().DownloadFile(id);
        }

    }
}
